from bzrc import BZRC, Command, ShootCommand
import sys, math, time, optparse, random, pprint


class Agent(object):

	def __init__(self, bzrc):
		self.bzrc = bzrc
		self.constants = self.bzrc.get_constants()
		self.commands = []
		self.state = []
		# Seconds since last state change
		self.lastStateChanged = []
		# Seconds since last shot
		self.lastShot = []
		# Desired turn angle
		self.desiredAngle = []
		# Desired position
		self.desiredPos = []

	def tick(self, time_diff):
		'''Some time has passed; decide what to do next'''
		# Get information from the BZRC server
		mytanks, othertanks, flags, shots = self.bzrc.get_lots_o_stuff()
		self.mytanks = mytanks
		self.othertanks = othertanks
		self.flags = flags
		self.shots = shots
		self.enemies = [tank for tank in othertanks if tank.color !=
				self.constants['team']]
		if len(self.lastStateChanged) == 0:
			self.lastStateChanged = [0 for tank in mytanks]
			self.lastShot = [0 for tank in mytanks]
			self.state = ["move" for tank in mytanks]
			self.desiredAngle = [0 for tank in mytanks]
			self.desiredPos = [(0, 0) for tank in mytanks]

		# Reset my set of commands (we don't want to run old commands)
		self.commands = []

		# Decide what to do with each of my tanks
		for bot in mytanks:
			self.be_dumb(bot, time_diff)

		# Send the commands to the server
		results = self.bzrc.do_commands(self.commands)

	def be_dumb(self, bot, time_diff):
		if self.state[bot.index] == "move":
			# Build a tuple that will handle our target position
			command = Command(bot.index, 1, 0)
			self.commands.append(command)
			# How long have we been driving?
			if self.lastStateChanged[bot.index] + time_diff > random.randint(3,8):
				self.change_state(bot.index, "turn")
				self.desiredAngle[bot.index] = self.normalize_angle(bot.angle + 2 * math.pi / 3)
			else:
				self.lastStateChanged[bot.index] += time_diff
		elif self.state[bot.index] == "turn":
			delAngle = self.desiredAngle[bot.index] - self.normalize_angle(bot.angle)
			if math.fabs(delAngle) > 0.5:
				relative_angle = self.normalize_angle(self.desiredAngle[bot.index] - self.normalize_angle(bot.angle))
				command = Command(bot.index, 1, 2 * relative_angle)
				self.commands.append(command)
				self.lastStateChanged[bot.index] += time_diff
			else:
				self.change_state(bot.index, "move")
		if self.lastShot[bot.index] + time_diff > random.uniform(1.5, 2.5):
			command = ShootCommand(bot.index)
			self.commands.append(command)
		else:
			self.lastShot[bot.index] += time_diff


	def change_state(self, index, newState):
		#print "Changing ", index, "'s state to " + newState
		self.state[index] = newState
		self.lastStateChanged[index] = 0

	def attack_enemies(self, bot):
		'''Find the closest enemy and chase it, shooting as you go'''
		best_enemy = None
		best_dist = 2 * float(self.constants['worldsize'])
		for enemy in self.enemies:
			if enemy.status != 'alive':
				continue
			dist = math.sqrt((enemy.x - bot.x)**2 + (enemy.y - bot.y)**2)
			if dist < best_dist:
				best_dist = dist
				best_enemy = enemy
		if best_enemy is None:
			command = Command(bot.index, 0, 0)
			self.commands.append(command)
		else:
			self.move_to_position(bot, best_enemy.x, best_enemy.y)

	def move_to_position(self, bot, target_x, target_y):
		target_angle = math.atan2(target_y - bot.y,
				target_x - bot.x)
		relative_angle = self.normalize_angle(target_angle - bot.angle)
		command = Command(bot.index, 1, 2 * relative_angle)
		self.commands.append(command)

	def normalize_angle(self, angle):
		'''Make any angle be between +/- pi.'''
		angle -= 2 * math.pi * int (angle / (2 * math.pi))
		if angle <= -math.pi:
			angle += 2 * math.pi
		elif angle > math.pi:
			angle -= 2 * math.pi
		return angle


def main():
	# Process CLI arguments.
	parser = optparse.OptionParser(usage = "%prog [options]",
								   version = "%prog 0.1")
	parser.add_option("-o", "--host", type="string",dest="host",
			metavar="HOST",default='localhost',
			help="hostname for the server")
	parser.add_option("-p", "--port", type="int",dest="port",
			metavar="PORT",default=3907,
			help="port number for the server")

	(options, args) = parser.parse_args()

	# Connect.
	#bzrc = BZRC(options.host, int(options.port), debug=True)
	bzrc = BZRC(options.host, int(options.port))

	agent = Agent(bzrc)

	prev_time = time.time()

	# Run the agent
	try:
		while True:
			time_diff = time.time() - prev_time
			agent.tick(time_diff)
			prev_time = time.time()
			time.sleep(.1)
	except KeyboardInterrupt:
		print "Exiting due to keyboard interrupt."
		bzrc.close()


if __name__ == '__main__':
	main()

# vim: et sw=4 sts=4
